#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>

#include <stdexcept>
#include <string>

#include "symdb-conf.h"
#include "filepath-util.h"
#include "autocall-util.h"
#include "string-util.h"
#include "io-util.h"
#include "log-util.h"

using std::string;
using std::runtime_error;


namespace {

SymdbConf *g_symdb_conf_ptr = NULL;

void destroy_g_symdb_conf_ptr()
{
    if (g_symdb_conf_ptr != NULL) {
        delete g_symdb_conf_ptr;
        g_symdb_conf_ptr = NULL;
    }
    return ;
}

AUTOCALL(destroy_g_symdb_conf_ptr);

/*
 * 检查 path 的合法性,是否可以作为默认数据库的路径.具体检察以下项:
 * 1. path 的目录部分必须存在.
 * @param path 此时 path 是已经经过 realpath() 后的绝对路径.
 * @return 若 path 合法,则正常返回;否则抛出异常.
 */
void
check_default_db_path(const char *path)
{
    const char *last_slash_ptr = strrchr(path,'/');
    if (last_slash_ptr == NULL)
        throw std::runtime_error(format("路径 %s 不合法;",path));
    string path_dir{path,last_slash_ptr};
    if (path_dir.empty())
        path_dir = "/";
    if (!PathUtil::isDir(path_dir.c_str())) {
        throw std::runtime_error(format("%s 不是目录!无法在其下面创建数据库",
                                        path_dir.c_str()));
    }
    return ;
}

// 指定了 symdb 配置文件所处目录的目录名,只是一个名字,并不包括路径信息
const char *CONF_DIR_NAME = ".symdb";
// 指定了 symdb 配置文件的文件名,只是一个名字,并不包括路径信息.
const char *CONF_FILE_NAME = "symdb.conf";

// 创建存放 symdb 配置的目录;并返回配置目录的路径.
string
symdb_conf_dir()
{
    const char *home = getenv("HOME");
    if (home == NULL)
        throw std::runtime_error("无法通过环境变量 HOME 获取用户主目录");
    string conf_dir{home};
    conf_dir.append(1,'/');
    conf_dir.append(CONF_DIR_NAME);

    if (!PathUtil::isDir(conf_dir.c_str())) {
        int ret = mkdir(conf_dir.c_str(),0775);
        if (ret != 0) {
            throw std::runtime_error(format("无法创建配置目录 %s;错误: %s",
                                            conf_dir.c_str(),strerror(errno)));
        }
    }

    return conf_dir;
}

// 获取配置文件的路径.此时文件可能不存在.
string
symdb_get_conf_file_path()
{
    string conf_file_path {symdb_conf_dir()};
    conf_file_path.append(1,'/');
    conf_file_path.append(CONF_FILE_NAME);
    return conf_file_path;
}


} // namespace ;

void SymdbConf::setDbPath(const char *path)
{
    string path_real_path {PathUtil::realpath(path)};
    check_default_db_path(path_real_path.c_str());
    db_path_ = path_real_path;
//    writeConfToFile();
    return ;
}

const std::string& SymdbConf::dbPath()
{
    if (db_path_.empty()) {
        loadConfFromFile();
        check_default_db_path(db_path_.c_str());
    }
    return db_path_;
}

SymdbConf& SymdbConf::instance()
{
    if (g_symdb_conf_ptr == NULL)
        g_symdb_conf_ptr = new SymdbConf();
    return *g_symdb_conf_ptr;
}

void SymdbConf::writeConfToFile() const
{
    string conf_file_path = symdb_get_conf_file_path();
    write(conf_file_path.c_str(),db_path_);
    return ;
}

void SymdbConf::loadConfFromFile()
{
    db_path_.clear();

    string conf_file_path = symdb_get_conf_file_path();
    read(conf_file_path.c_str(),db_path_);
    return ;
}

